package cn.skyquant.quant4j.jforex.strategy;

import cn.skyquant.quant4j.api.enums.VType;
import cn.skyquant.quant4j.api.quant.Bar;
import cn.skyquant.quant4j.api.quant.TimeBar;
import cn.skyquant.quant4j.api.quant.VBar;
import cn.skyquant.quant4j.jforex.sdk.strategy.AbstractStrategy;
import cn.skyquant.quant4j.jforex.sdk.util.QuantUtil;
import cn.skyquant.quant4j.sdk.util.time.CalendarUtils;
import com.dukascopy.api.*;
import com.dukascopy.api.feed.IBarFeedListener;

import java.util.ArrayList;
import java.util.List;

/**
 * 波塞顿
 * 波浪策略
 * @author harley-dog
 */
@Library("quant4j-api.jar;quant4j-sdk.jar;quant4j-jforex-sdk.jar;freemarker-2.3.28.jar;httpclient-4.5.6.jar;httpcore-4.4.10.jar;fastjson-1.2.49.jar;commons-codec-1.11.jar")
public class Poseidon extends AbstractStrategy implements IBarFeedListener {

    @Configurable("交易品种")
    public Instrument instrument = Instrument.EURUSD;
    @Configurable("交易方向")
    public OfferSide offerSide = OfferSide.ASK;

//    @Configurable("A-B[x]")
//    public double x = 6;
//
//    @Configurable("B-C[y]")
//    public double y = 2;

    @Configurable("最多计算天数")
    public int max_days = 200;

    @Configurable("计算V的K柱个数,必须单数")
    public int v_days = 3;


    private static int LEAST_DAYS = 30;

    @Override
    protected String getVersion() {
        return "001";
    }

    @Override
    protected String getName() {
        return "Poseidon";
    }

    @Override
    protected String getEaName() {
        return "Poseidon";
    }

    @Override
    protected void onStartEx() {
        if(max_days<LEAST_DAYS){
            err("calc_days = %d < least %d days,",max_days,LEAST_DAYS);
            context.stop();
        }
        if(v_days%2 == 0){
            err("v_days must be odd number");
            context.stop();
        }
        context.subscribeToBarsFeed(instrument, period, offerSide, this);//逆市运行间隔
    }


    @Override
    public void onBar(Instrument instrument, Period period, OfferSide offerSide, IBar bar) {
        WaveDTO wave = WaveDTO.get(instrument,context.getTime());
        try {
            List<IBar> ibarList = history.getBars(instrument,period,this.offerSide,Filter.WEEKENDS,max_days,bar.getTime(),0);//最多计算200根K柱
            List<TimeBar> timeBarList = QuantUtil.batchConvertIBarToTimeBar(ibarList);
            if(timeBarList.size()<LEAST_DAYS){
                return;
            }
            int size = timeBarList.size();
            TimeBar bar0 = timeBarList.get(size-1);
            wave.d = bar0;
            List<VBar> vBarList = new ArrayList<>();
            for(int i=2;i<size-2;i++){
                boolean v = TimeBar.isV1(timeBarList,i,v_days);
                boolean rv = TimeBar.isRV1(timeBarList,i,v_days);
                if(v) vBarList.add(new VBar(timeBarList.get(i),VType.V,i));
                if(rv) vBarList.add(new VBar(timeBarList.get(i),VType.RV,i));
            }

            vBarList.forEach(x->out("vbar=%s",x));
            //根据这些vBar开始找
            //先找第一个比当前价格低的v或者比当前价格高的rv,记录c点
            for(int i=vBarList.size()-1;i>=0;i--){
                VBar vBar = vBarList.get(i);
                if(wave.c == null){
                    //第一步找c
                    if(vBar.vType == VType.V && vBar.low <= TimeBar.getMinLow(vBar.i,size-1,timeBarList)){
                        wave.c = vBar;
                    }else if(vBar.vType == VType.RV && vBar.high >= TimeBar.getMaxHigh(vBar.i,timeBarList.size()-1,timeBarList)){
                        wave.c = vBar;
                    }else {
                        continue;
                    }
                }else if(wave.b == null){
                    //找到c之后找b
                    if(wave.c.vType == VType.V){
                        //如果cType=V，那么b要找RV，如果还是找到V，并且low更低，那么就继续将low赋给c
                        if(vBar.vType==VType.V && vBar.low <= TimeBar.getMinLow(vBar.i,wave.c.i,timeBarList) ){
                            wave.c = vBar;
                        }else if(vBar.vType == VType.RV){
                            wave.b = vBar;
                        }
                    }
                    if(wave.c.vType == VType.RV){
                        //如果cType=V，那么b要找RV，如果还是找到V，那么就继续将low赋给b
                        if(vBar.vType==VType.RV && vBar.high >= TimeBar.getMaxHigh(vBar.i,wave.c.i,timeBarList)){
                            wave.c = vBar;
                        }else if(vBar.vType == VType.V){
                            wave.b = vBar;
                        }
                    }
                }else if(wave.a == null){
                    //找到b之后找a
                    if(wave.b.vType == VType.V){
                        //如果cType=V，那么b要找RV，如果还是找到V，那么就继续将low赋给a
                        if(vBar.vType==VType.V && vBar.low <= TimeBar.getMinLow(vBar.i,wave.b.i,timeBarList)){
                            wave.b = vBar;
                        }else if(vBar.vType == VType.RV){
                            wave.a = vBar;
                        }
                    }
                    if(wave.b.vType == VType.RV){
                        //如果cType=V，那么b要找RV，如果还是找到V，那么就继续将low赋给c
                        if(vBar.vType==VType.RV && vBar.high >= TimeBar.getMaxHigh(vBar.i,wave.b.i,timeBarList)){
                            wave.b = vBar;
                        }else if(vBar.vType == VType.V){
                            wave.a = vBar;
                        }
                    }
                }else {
                    //a也找到了，继续加深a,如果找到跟a反向的，就退出循环
                    if(wave.a.vType == VType.V){
                        if(vBar.vType == VType.V && vBar.low <= TimeBar.getMinLow(vBar.i,wave.a.i,timeBarList)){
                            wave.a = vBar;
                        }else if(vBar.vType == VType.RV){
                            break;
                        }
                    }
                    if(wave.a.vType==VType.RV){
                        if(vBar.vType == VType.RV && vBar.high >= TimeBar.getMaxHigh(vBar.i,wave.a.i,timeBarList)){
                            wave.a = vBar;
                        }else if(vBar.vType == VType.V){
                            break;
                        }
                    }
                }
            }
        } catch (JFException e) {
            err("%s getBar 1 error:%s", instrument.name(),e.getMessage());
        }
        out("current Time:%s",CalendarUtils.formatSimple(wave.time));
        out("d=%s",wave.d);
        out("c=%s",wave.c);
        out("b=%s",wave.b);
        out("a=%s",wave.a);
    }

    @Override
    protected void onTickEx(Instrument instrument, ITick tick) {
    }

    @Override
    protected void onBarEx(Instrument instrument, Period period, IBar askBar, IBar bidBar) {
    }


    @Override
    protected void onMessageEx(IMessage message) {
    }

    @Override
    protected void onAccountEx(IAccount account) {
    }

    @Override
    protected void onStopEx() {
    }


    static class WaveDTO{
        public VBar a;
        public VBar b;
        public VBar c;
        public TimeBar d;
        public long time;
        public Instrument instrument;

        public static WaveDTO get(Instrument instrument,long time){
            WaveDTO waveDTO = new WaveDTO();
            waveDTO.instrument = instrument;
            waveDTO.time = time;
            return waveDTO;
        }
        @Override
        public String toString() {
            return String.format("%s.wave %s[a=(%s),b=(%s),c=(%s),d=(%s)]",
                    instrument.name(),CalendarUtils.formatStandard(time),a,b,c,d);
        }
    }
}
